﻿#region LGPL License
/*----------------------------------------------------------------------------
* This file (Features\SystemEventWatcher\SystemEventWatcher.cs) is part of CiviKey. 
*  
* CiviKey is free software: you can redistribute it and/or modify 
* it under the terms of the GNU Lesser General Public License as published 
* by the Free Software Foundation, either version 3 of the License, or 
* (at your option) any later version. 
*  
* CiviKey is distributed in the hope that it will be useful, 
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
* GNU Lesser General Public License for more details. 
* You should have received a copy of the GNU Lesser General Public License 
* along with CiviKey.  If not, see <http://www.gnu.org/licenses/>. 
*  
* Copyright © 2007-2009, 
*     Invenietis <http://www.invenietis.com>,
*     In’Tech INFO <http://www.intechinfo.fr>,
* All rights reserved. 
*-----------------------------------------------------------------------------*/
#endregion

//****************************************************
//Author : Antoine Blanchet
//Mail : blanchet@intechinfo.fr
//Date : 24-06-2009
//****************************************************
using System;
using System.Collections.Generic;
using System.Text;
using System.Management;
using System.Diagnostics;
using System.Timers;
using System.Runtime.InteropServices;
using CK.Plugin;
using CommonServices;
using ContribServices;


namespace SystemEventWatcher
{
    [Plugin(Id = "{8EF36725-FEF1-45d5-8806-B00B0921B864}",
        PublicName = "SystemEventWatcher",
        Version = "1.0.0",
        DefaultPluginStatus = ConfigPluginStatus.Manual,
        Categories = new string[] { "Accessibility" })]

    public class SystemEventWatcher : IPlugin, ISystemEventWatcher
    {
        #region Variables
        private ManagementEventWatcher _startWatch;
        private ManagementEventWatcher _stopWatch;
        public static int foregroundWindow;
        #endregion
        #region timer
        private ICommonTimer _timer;
        private int _timerIncrement;
        /// <summary>
        /// Property use to compare the current foreground window.
        /// When the current foreground window is different to the older, an event is 
        /// </summary>
        [Service(Requires = ServiceRequirements.MustExistAndRun)]
        public ICommonTimer Timer
        {
            get { return _timer; }
            set { _timer = value; }
        }
        #endregion

        /// <summary>
        /// Start the plugin
        /// </summary>
        public void Start()
        {
            Timer.Tick += new EventHandler(Timer_Tick);

            _startWatch = new ManagementEventWatcher(new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace"));
            _stopWatch = new ManagementEventWatcher(new WqlEventQuery("SELECT * FROM Win32_ProcessStopTrace"));

            //Abonnements aux évenements de startWatch et stopWatch
            _startWatch.EventArrived += new EventArrivedEventHandler(startWatch_EventArrived);
            _stopWatch.EventArrived += new EventArrivedEventHandler(stopWatch_EventArrived);

            _startWatch.Start();
            _stopWatch.Start();
        }
        /// <summary>
        /// Stop the plugin
        /// </summary>
        public void Stop()
        {
            _stopWatch.Stop();
            _startWatch.Stop();

            //Désabonnements aux évenements de startWatch et stopWatch
            _startWatch.EventArrived -= new EventArrivedEventHandler(startWatch_EventArrived);
            _stopWatch.EventArrived -= new EventArrivedEventHandler(stopWatch_EventArrived);

            _startWatch = null;
            _stopWatch = null;
        }

        public bool CanStart(out string message)
        {
            message = "";
            return true;
        }

        public bool Setup(ISetupInfo info)
        {
            return true;
        }

        public void Teardown()
        {

        }

        #region Watch process start/stop
        /// <summary>
        /// What we do when a process is destroy
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void stopWatch_EventArrived(object sender, EventArrivedEventArgs e)
        {
            int processId = Convert.ToInt16(e.NewEvent.Properties["ProcessId"].Value);
            ProcessEventArgs args = new ProcessEventArgs(processId);
            if (ProcessStop != null)
            {
                //Send the process stop event
                ProcessStop(this, args);
            }
        }
        /// <summary>
        /// What we do when a new process is create.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void startWatch_EventArrived(object sender, EventArrivedEventArgs e)
        {
            int processId = Convert.ToInt16(e.NewEvent.Properties["ProcessId"].Value);
            ProcessEventArgs args = new ProcessEventArgs(processId);
            if (ProcessStart != null)
            {
                //Send the process start event
                ProcessStart(this, args);
            }
        }
        #endregion
        #region Watch foreground window
        /// <summary>
        /// Method call for each timer's tick
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Timer_Tick(object sender, EventArgs e)
        {
            if (this._timerIncrement == 5)
            {
                this._timerIncrement = 0;
                CheckForegroundWindow();
            }
            this._timerIncrement++;
        }
        /// <summary>
        /// This method will retrieve the process id of the foreground window
        /// </summary>
        /// <returns>process id of the foreground window</returns>
        private static int GetForegroundApplication()
        {
            int processID;
            GetWindowThreadProcessId(GetForegroundWindow(), out processID);
            return processID;
        }
        /// <summary>
        /// Compare the current foreground window id with the previous id. If they're differents a changeForegroundWindow event is send
        /// </summary>
        private void CheckForegroundWindow()
        {
            if (GetForegroundApplication() != foregroundWindow)
            {
                foregroundWindow = GetForegroundApplication();
                Console.Out.WriteLine(foregroundWindow);
                Process currentProcessById = Process.GetProcessById(foregroundWindow);
                //Debug.WriteLine(currentProcessById.ProcessName);
                //ChangeForegroundWindowEventArgs eventArgs = new ChangeForegroundWindowEventArgs((int)foregroundWindow);
                ChangeForegroundWindowEventArgs eventArgs = new ChangeForegroundWindowEventArgs(currentProcessById);
                if (ChangeForegroundWindow != null)
                {
                    //Send the change foureground window event
                    ChangeForegroundWindow(this, eventArgs);
                }
            }
        }
        #endregion

        #region Import user32.dll functions
        [DllImport("user32.dll")]
        public static extern IntPtr GetForegroundWindow();
        [DllImport("user32.dll")]
        public static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
        #endregion
        #region ISystemEventWatcher

        public event ChangeForegroundWindowEventHandler ChangeForegroundWindow;
        public event ProcessStartEventHandler ProcessStart;
        public event ProcessStopEventHandler ProcessStop;

        #endregion
    }
}